﻿using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Xml;
using System.Xml.Linq;
using Sonce.Common;
using Sonce.Controls;
using Sonce.Editor;
using Microsoft.Windows.Controls;
using TreeView = Microsoft.Windows.Controls.TreeView;
using TreeViewItem = Microsoft.Windows.Controls.TreeViewItem;

namespace Sonce.Core
{
    /// <summary>
    /// This class handles loading menu, toolbar, tree, database, etc.
    /// </summary>
    public partial class Loader : Sonce.Common.Loader
    {
        #region Enums
        /// <summary>
        /// Defines path type - embedded resource, relative external, absolute external path.
        /// </summary>
        public enum Sources
        {
            Resources,
            //RelativeUri,
            //AbsoluteUri,
        }
        #endregion

        #region Menu
        /// <summary>
        /// Load menu by getting and parsing xml and adding items recursively.
        /// </summary>
        /// <param name="menu">[ref] Menu instance where items should be loaded to.</param>
        /// <param name="path">[in] Path to a menu xml file.</param>
        /// <param name="source">[in] Defines path type - embedded resource, relative external, absolute external path.</param>
        /// <returns>Returns true if menu items have been successfully loaded.</returns>
        public static bool LoadMenu(ref Menu menu, string path, Sources source)
        {
            bool completed = false;
            switch (source)
            {
                case Sources.Resources:
                    completed = LoadMenu(ref menu, Loader.GetResourceStream(AssemblyNames.Resource, path));
                    break;

                default:
                    throw new NotImplementedException("Source '" + source + "' not implemented!");
            }
            return completed;
        }

        /// <summary>
        /// Load menu by getting and parsing xml and adding items recursively.
        /// </summary>
        /// <param name="menu">[ref] Menu instance where items should be loaded to.</param>
        /// <param name="xmlStream">[in] Stream that represents menu xml content.</param>
        /// <returns>Returns true if menu items have been successfully loaded.</returns>
        public static bool LoadMenu(ref Menu menu, Stream xmlStream)
        {
            bool completed = false;
            try
            {
                menu.Items.Clear();
                XDocument doc = XDocument.Load(xmlStream);
                foreach (XElement element in doc.Root.Elements("item"))
                {
                    XAttribute title = element.Attribute("title");
                    MenuItem menuItem = new MenuItem();
                    menuItem.Header = (title != null ? title.Value : null);
                    menuItem.ParentMenu = menu;
                    if (element.HasElements)
                    {
                        //Recursively load menu items
                        LoadMenuItem(ref menuItem, element);
                    }
                    menu.Items.Add(menuItem);
                }
            }
            catch (Exception ex)
            {
                completed = false;
                Logger.Log(ex, (int)LogLevels.Normal);
            }
            return completed;
        }

        /// <summary>
        /// Load menu items for one level (all subitems for a parant item). This method is called recursively.
        /// </summary>
        /// <param name="menuItem">[ref] Parent MenuItem instance.</param>
        /// <param name="element">[in] Xml node that represents the parent menu item.</param>
        /// <returns>Returns true if all items have been successfully loaded.</returns>
        public static bool LoadMenuItem(ref MenuItem menuItem, XElement element)
        {
            bool completed = true;
            foreach (XElement item in element.Elements())
            {
                try
                {
                    switch (item.Name.ToString())
                    {
                        case "item":
                            MenuItem subMenuItem = new MenuItem();
                            XAttribute title = item.Attribute("title");
                            XAttribute tooltip = item.Attribute("tooltip");
                            XAttribute command = item.Attribute("command");
                            XAttribute value = item.Attribute("value");
                            XAttribute visible = item.Attribute("visible");
                            XAttribute icon = item.Attribute("icon");

                            if (visible == null || visible.Value == "true")
                            {
                                Command tag = new Command();
                                tag.Name = (command != null ? command.Value : null);
                                tag.Value = (value != null ? value.Value : null);

                                subMenuItem.Tag = tag;
                                subMenuItem.Header = (title != null ? title.Value : null);
                                //subMenuItem.Icon = Loader.GetResourceUri(AssemblyNames.Resource, (icon != null ? icon.Value : null));
                                subMenuItem.ParentMenu = menuItem.ParentMenu;

                                if (item.HasElements)
                                {
                                    LoadMenuItem(ref subMenuItem, item);
                                }

                                menuItem.Items.Add(subMenuItem);
                            }
                            break;

                        case "separator":
                            MenuSeparator separator = new MenuSeparator();
                            //separator.Visibility = Visibility.Visible
                            menuItem.Items.Add(separator);
                            break;

                        default:
                            completed = false;
                            break;
                    }
                }
                catch
                {
                    completed = false;
                }
            }
            return completed;
        }
        #endregion

        #region Toolbar
        /// <summary>
        /// Load toolbar by getting and parsing xml and adding items.
        /// </summary>
        /// <param name="menu">[ref] Toolbar instance where items should be loaded to.</param>
        /// <param name="path">[in] Path to a toolbar xml file.</param>
        /// <param name="source">[in] Defines path type - embedded resource, relative external, absolute external path.</param>
        /// <returns>Returns true if toolbar items have been successfully loaded.</returns>
        public static bool LoadToolbar(ref Toolbar toolbar, string path, Sources source)
        {
            bool completed = false;
            switch (source)
            {
                case Sources.Resources:
                    completed = LoadToolbar(ref toolbar, Loader.GetResourceStream(AssemblyNames.Resource, path));
                    break;

                default:
                    throw new NotImplementedException("Source '" + source + "' not implemented!");
            }
            return completed;
        }

        /// <summary>
        /// Load toolbar by getting and parsing xml and adding items.
        /// </summary>
        /// <param name="toolbar">[ref] Toolbar instance where items should be loaded to.</param>
        /// <param name="xmlStream">[in] Stream that represents toolbar xml content.</param>
        /// <returns>Returns true if toolbar items have been successfully loaded.</returns>
        public static bool LoadToolbar(ref Toolbar toolbar, Stream xmlStream)
        {
            bool completed = false;
            try
            {
                toolbar.Items.Clear();
                XDocument doc = XDocument.Load(xmlStream);
                foreach (XElement tool in doc.Root.Descendants("tools"))
                {
                    foreach (XElement item in tool.Descendants("tool"))
                    {
                        ToolbarButton button = new ToolbarButton();
                        XAttribute tooltip = item.Attribute("tooltip");
                        XAttribute command = item.Attribute("command");
                        XAttribute value = item.Attribute("value");
                        XAttribute icon = item.Attribute("icon");

                        Command tag = new Command();
                        tag.Name = (command != null ? command.Value : null);
                        tag.Value = (value != null ? value.Value : null);

                        button.Tag = tag;
                        button.Icon = Loader.GetResourceUri(AssemblyNames.Resource, (icon != null ? icon.Value : null));
                        ToolTipService.SetToolTip(button, tooltip != null ? tooltip.Value : null);
                        toolbar.Items.Add(button);
                        completed = true;
                    }
                    toolbar.Items.Add(new ToolbarSeparator());
                }

                if (toolbar.Items.Count > 0)
                {
                    //Remove last separator
                    toolbar.Items.RemoveAt(toolbar.Items.Count - 1);
                }
            }
            catch (Exception ex)
            {
                completed = false;
                Logger.Log(ex, (int)LogLevels.Normal);
            }
            return completed;
        }
        #endregion

        #region Tree
        /// <summary>
        /// Load a tree by getting and parsing xml and adding items recursively.
        /// </summary>
        /// <param name="menu">[ref] TreeView instance where items should be loaded to.</param>
        /// <param name="path">[in] Path to a tree xml file.</param>
        /// <param name="source">[in] Defines path type - embedded resource, relative external, absolute external path.</param>
        /// <returns>Returns true if tree items have been successfully loaded.</returns>
        public static bool LoadLibrary(ref TreeView tree, string path, Sources source)
        {
            bool completed = false;
            switch (source)
            {
                case Sources.Resources:
                    completed = LoadLibrary(ref tree, Loader.GetResourceStream(AssemblyNames.Resource, path));
                    break;

                default:
                    throw new NotImplementedException("Source '" + source + "' not implemented!");
            }
            return completed;
        }

        /// <summary>
        /// Load a tree by getting and parsing xml and adding items recursively.
        /// </summary>
        /// <param name="menu">[ref] TreeView instance where items should be loaded to.</param>
        /// <param name="path">[in] Stream that represents tree xml content.</param>
        /// <returns>Returns true if tree items have been successfully loaded.</returns>
        public static bool LoadLibrary(ref TreeView tree, Stream xmlStream)
        {
            bool completed = false;

            try
            {
                tree.Items.Clear();
                using (XmlReader reader = XmlReader.Create(xmlStream))
                {
                    bool isLibrary = false;
                    while (reader.Read())
                    {
                        //Read starting tag
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            //Check for root node
                            if (reader.LocalName == "library")
                            {
                                isLibrary = true;
                                continue;
                            }

                            //If it is valid library xml
                            if (isLibrary)
                            {
                                //Parse <ref>
                                if (reader.LocalName == "ref" && reader.HasAttributes)
                                {
                                    string id = reader.GetAttribute("id");
                                    string source = reader.GetAttribute("source");
                                }
                                //Parse <node> recursively
                                else if (reader.LocalName == "item")
                                {
                                    TreeViewItem root = new TreeViewItem();
                                    parseNode(ref root, reader);
                                    tree.Items.Add(root);
                                    completed = true;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                completed = false;
                //Logger.Log(ex);
            }

            return completed;
        }

        /// <summary>
        /// Parse a tree node and attach subnodes recursively.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="reader"></param>
        private static void parseNode(ref TreeViewItem node, XmlReader reader)
        {
            //Parse attributes
            if (reader.HasAttributes)
            {
                string id = reader.GetAttribute("id");
                string clone = reader.GetAttribute("clone");
                string name = reader.GetAttribute("name");
                string title = reader.GetAttribute("title");
                string tooltip = reader.GetAttribute("tooltip");
                string source = reader.GetAttribute("source");
                string visible = reader.GetAttribute("visible");
                string command = reader.GetAttribute("command");
                string valueAttribute = reader.GetAttribute("value");
                string expanded = reader.GetAttribute("expanded");

                //Apply parsed attributes
                node.Header = title;
                //node.Title = title;
                ToolTipService.SetToolTip(node, tooltip);

                int value = 0;
                int.TryParse(id, out value);

                if (!string.IsNullOrEmpty(source))
                {
                    ElementTag tag = new ElementTag();
                    tag.Name = command;
                    tag.Value = valueAttribute;
                    tag.Source = source;
                    node.Tag = tag;
                }
                else
                {
                    Command tag = new Command();
                    tag.Name = command;
                    tag.Value = valueAttribute;
                    node.Tag = tag;
                }

                node.Visibility = (visible != "false" ? Visibility.Visible : Visibility.Collapsed);
                node.IsExpanded = (expanded == "true");

                #region Insert element prototype into database
                //NOTE: Insert at first use (store only recent used elements)
                //try
                //{
                //    var elementBases =
                //        from e in Repository.ElementPrototypes
                //        where e.ID == value
                //        select e;

                //    if (elementBases.Count() == 0)
                //    {
                //        Repository.ElementPrototypes.Insert(new ElementPrototype()
                //        {
                //            ID = value,
                //            Name = title,
                //            Image = Loader.LoadResourceString(source),
                //            Nodes = new List<Point>()
                //        });
                //    }
                //}
                //catch (Exception ex)
                //{
                //    Logger.Log(ex, (int)LogLevels.VeryDetailed);
                //}
                #endregion

                //Replace ref id string with ref source string
                source = applyReferences(source);
                if (!string.IsNullOrEmpty(source))
                {
                    if (source.StartsWith("http"))
                    {
                        //node.Source = new Uri(source);
                    }
                    else
                    {
                        //node.Source = new Uri(AppConfig.ApplicationBasePath + source);
                    }
                }

                //Show or hide node
                if (!string.IsNullOrEmpty(visible))
                {
                    node.Visibility = (visible.ToLower() == "true" ? Visibility.Visible : Visibility.Collapsed);
                }

                //if (!string.IsNullOrEmpty(clone))
                //{
                //    tree.libraryHasClones = true;
                //}
            }

            //Parse subnodes
            if (!reader.IsEmptyElement)
            {
                using (XmlReader subreader = reader.ReadSubtree())
                {
                    bool isSubnode = false;
                    while (subreader.Read())
                    {
                        if (subreader.NodeType == XmlNodeType.Element && subreader.LocalName == "item")
                        {
                            //Do not read first node 'coz it may trigger StackOverflowException
                            if (isSubnode)
                            {
                                TreeViewItem subnode = new TreeViewItem();
                                parseNode(ref subnode, subreader); //Recursion
                                node.Items.Add(subnode);

                                //If this node is final node
                                if (subreader.IsEmptyElement)
                                {
                                    //subnode.NodeClick += new RoutedEventHandler(subnode_NodeClick);
                                    //subnode.MouseLeftButtonDown += new MouseButtonEventHandler(node_MouseLeftButtonDown);
                                }
                            }
                            isSubnode = true;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Attach a reference item to a tree node. Reserved for future use.
        /// </summary>
        /// <param name="attribute"></param>
        /// <returns></returns>
        private static string applyReferences(string attribute)
        {
            //Example: replace 'ref1#Capacitor' with 'Elements.xaml#Capacitor'
            string result = attribute;
            //if (!string.IsNullOrEmpty(attribute) && tree.libraryReferences.Count > 0)
            {
                //foreach (LibraryReference reference in this.libraryReferences)
                //{
                //    if (attribute.StartsWith(reference.ID))
                //    {
                //        result = reference.Source + attribute.Substring(reference.ID.Length);
                //        break;
                //    }
                //}
            }
            return result;
        }
        #endregion

        #region Database
        public static void LoadDatabase(string path, Sources source)
        {
            //Not implemented
            //Repository.Import("Xml/Database.xml");
        }
        #endregion
    }
}
